package cn.kivensoft.util;


import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

public final class IoUtils {
	private IoUtils() {}

	/** 自动流拷贝
	 * @param in 输入流
	 * @param out 输出流
	 * @param autoClose 自动关闭标志
	 * @throws IOException 异常
	 */
	public static void transTo(InputStream in, OutputStream out, boolean autoClose) throws IOException {
		transTo(in, out, 4096, autoClose);
	}

	/** 自动流拷贝
	 * @param in 输入流
	 * @param out 输出流
	 * @param bufSize 拷贝使用的缓冲区大小
	 * @param autoClose 自动关闭标志
	 * @throws IOException 异常
	 */
	public static void transTo(InputStream in, OutputStream out, int bufSize, boolean autoClose) throws IOException {
		byte[] buf = new byte[bufSize];
		try {
			int count;
			while ((count = in.read(buf)) > 0)
				out.write(buf, 0, count);
		} finally {
			if (autoClose) {
				try { in.close(); } catch (Exception ignored) {}
				try { out.close(); } catch (Exception ignored) {}
			}
		}
	}

	/** 读取文件内容到字节数组
	 * @param filename 文件名
	 * @return 文件内容的字节数组
	 * @throws IOException 异常
	 */
	public static byte[] readFromFile(String filename) throws IOException {
		File f = new File(filename);
		if (!f.exists()) throw new FileNotFoundException();
		int len = (int) f.length();
		byte[] buf = new byte[len];
		if (len > 0) {
			try (FileInputStream fis = new FileInputStream(f)) {
				int pos = 0, rcount;
				while ((rcount = fis.read(buf, pos, len - pos)) > 0)
					pos += rcount;
			}
		}
		return buf;
	}

	/** 读取流内容到字节数组
	 * @param in 输入流
	 * @param autoClose 自动关闭标志
	 * @return 流内容字节数组
	 * @throws IOException 异常
	 */
	public static byte[] readFromStream(InputStream in, boolean autoClose) throws IOException {
		// 缓冲区长度必须为2的n次幂, 因为下面的计算中使用了位移算法
		final int BUF_SIZE = 4096;
		ArrayList<byte[]> list = new ArrayList<>();
		int readLength = 0, pos = 0, rlen;
		byte[] bytes = new byte[BUF_SIZE];
		try {
			while ((rlen = in.read(bytes, pos, BUF_SIZE - pos)) > 0) {
				readLength += rlen;
				pos += rlen;
				if (pos >= BUF_SIZE) {
					list.add(bytes);
					bytes = new byte[BUF_SIZE];
					pos = 0;
				}
			}

			if (pos > 0)
				list.add(bytes);
		} finally {
			if (autoClose) close(in);
		}

		byte[] ret = new byte[readLength];
		if (readLength > 0) {
			int maxIndex = list.size() - 1;
			// 先把所有读满的缓冲区复制到返回对象
			for (int i = 0; i < maxIndex; ++i)
				System.arraycopy(list.get(i), 0, ret, i << 12, BUF_SIZE);
			// 处理最后一块可能长度不满的缓冲区
			System.arraycopy(list.get(maxIndex), 0, ret,
					maxIndex << 12, readLength & (BUF_SIZE - 1));
		}

		return ret;
	}

	/** 读取文件内容到字符串
	 * @param filename 文件名
	 * @return 读取的字符串
	 * @throws IOException 异常
	 */
	public static String readStringFromFile(String filename) throws IOException {
		FileInputStream fis = new FileInputStream(filename);
		return readStringFromStream(fis, true);
	}

	/** 读取流内容到字符串(UTF8格式)
	 * @param in 输入流
	 * @param autoClose 是否自动关闭
	 * @return 流内容的字符串表示形式
	 * @throws IOException 异常
	 */
	public static String readStringFromStream(InputStream in, boolean autoClose) throws IOException {
		char[] buf = new char[4096];
		StringBuilder sb = new StringBuilder();
		try {
			Reader r = new InputStreamReader(in, StandardCharsets.UTF_8);
			int count;
			while ((count = r.read(buf)) > 0)
				sb.append(buf, 0, count);
		} finally {
			if (autoClose) close(in);
		}
		return sb.toString();
	}

	public interface ForEachStreamConsumer {
		void accept(byte[] readBytes, int offset, int length) throws Exception;
	}

	/** 批量处理输入流内容
	 * @param in 输入流
	 * @param func 回调处理接口, 参数(buf, count)
	 */
	public static void forEachStream(InputStream in, ForEachStreamConsumer func) throws Exception {
		forEachStream(in, 4096, true, func);
	}

	/** 批量处理输入流内容
	 * @param in 输入流
	 * @param bufSize 缓冲区大小
	 * @param autoClose 处理完成自动关闭标志
	 * @param func 回调处理接口, 参数(buf, count)
	 */
	public static void forEachStream(InputStream in, int bufSize, boolean autoClose, ForEachStreamConsumer func) throws Exception {
		byte[] buf = new byte[bufSize];
		try {
			int count;
			while ((count = in.read(buf)) > 0)
				func.accept(buf, 0, count);
		} finally {
			if (autoClose)
				try { in.close(); } catch (IOException ignored) {}
		}
	}

	/** 关闭资源，抛弃异常
	 * @param closeable 实现可关闭接口的对象
	 */
	public static void close(Closeable closeable) {
		if (closeable != null)
			try { closeable.close(); } catch(IOException ignored) {}
	}

	/** 关闭多个资源，抛弃异常
	 * @param closeables 多个实现可关闭接口的对象
	 */
	public static void close(Closeable... closeables) {
		// 双重嵌套循环，减少循环中频繁设置try/catch的性能损失
		for (int i = 0, len = closeables.length; i < len; ++i) {
			try {
				for (; i < len; ++i) {
					Closeable c = closeables[i];
					if (c != null) c.close();
				}
			}
			catch(IOException ignored) {}
		}
	}

	/** 采用反射的方式调用对象的close函数关闭资源，抛弃异常
	 * @param args 多个需要关闭的对象
	 */
	public static void close(Object...args) {
		// 双重嵌套循环，减少循环中频繁设置try/catch的性能损失
		for (int i = 0, len = args.length; i < len; ++i) {
			try {
				for (; i < len; ++i) {
					Object obj = args[i];
					if (obj == null) continue;
					Method m = obj.getClass().getMethod("close");
					if (m.getParameterCount() == 0)
						m.invoke(obj);
				}
			}
			catch(Exception ignored) {}
		}
	}

}
